/*
Copyright (c) 2010 julien barbay <barbay.julien@gmail.com>

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

package martian.m4gic.display 
{
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	import flash.utils.Dictionary;
	
	import martian.vo0doo.core.Behavior;
	import martian.m4gic.events.DisplayEvent;
	import martian.m4gic.events.UpdateEvent;
	import martian.m4gic.tools.Maths;
	
	public dynamic class Smartie extends MovieClip
	{
		[Event(name = UpdateEvent.X, 		type = "martian.vo0doo.events.UpdateEvent")]
		[Event(name = UpdateEvent.Y, 		type = "martian.vo0doo.events.UpdateEvent")]
		[Event(name = UpdateEvent.POSITION, type = "martian.vo0doo.events.UpdateEvent")]
		[Event(name = UpdateEvent.WIDTH, 	type = "martian.vo0doo.events.UpdateEvent")]
		[Event(name = UpdateEvent.HEIGHT, 	type = "martian.vo0doo.events.UpdateEvent")]
		[Event(name = UpdateEvent.SIZE,		type = "martian.vo0doo.events.UpdateEvent")]
		
		public function Smartie() {}
		
		//BEHAVIORS SUPPORT
		private var _behaviors:Array = new Array();
			public function get behaviors():Array { return _behaviors; }
			public function set behaviors(value:Array):void
			{
				if (_behaviors.length > 0) { forget(); }
				for each(var behavior:Behavior in value) { learn(behavior); }
			}
			
		public function learn(behavior:Behavior):Behavior
		{
			behavior.target = this;
			_behaviors.push(behavior);
			
			return behavior;
		}
		
		public function remember(name:String):Behavior
		{
			for (var i:int = 0; i < _behaviors.length; i++)
			{
				if (_behaviors[i].name == name) { return _behaviors[i]; }
			}
			
			return null;
		}
		
		public function forget(ref:Behavior = null):void
		{
			for (var i:int = 0; i < _behaviors.length; i++)
			{
				if ((ref == _behaviors[i]) || (ref == null))
				{
					_behaviors[i].desactivate();
					_behaviors[i].dispose();
					_behaviors[i] = null;
					
					_behaviors.splice(i, 1);
				}
			}
		}
		
		
		
		
		
		//POSITION/SIZE OVERRIDES
		override public function set x(value:Number):void 
		{
			if (x != value)
			{
				super.x = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.X));
			}
		}
		
		override public function set y(value:Number):void 
		{
			if (y != value)
			{
				super.y = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.Y));
			}
		}
		
		override public function set width(value:Number):void 
		{
			if (width != value)
			{
				super.width = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.WIDTH));
			}
		}
		
		override public function set height(value:Number):void 
		{
			if (height != value)
			{
				super.height = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.HEIGHT));
			}
		}
		
		override public function set scaleX(value:Number):void
		{
			if (scaleX != value)
			{
				super.scaleX = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.SCALEX));
			}
		}
		
		override public function set scaleY(value:Number):void
		{
			if (scaleY != value)
			{
				super.scaleY = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.SCALEY));
			}
		}
		
		public override function set rotation(value:Number):void 
		{
			if (rotation != value)
			{
				super.rotation = value;
				
				dispatchEvent(new UpdateEvent(UpdateEvent.ROTATION));
			}
		}
		
		public function get rect():Rectangle { return getRect(this); }
		
		public function get bounds():Rectangle
		{
			var rectangle:Rectangle = rect;
				
			if (mask != null) { rectangle = rectangle.intersection(mask.getRect(this)); }
			if (scrollRect != null) { rectangle = rectangle.intersection(scrollRect); }
			
			return rectangle;
		}
		
		
		
		
		
		
		
		
		//DISPLAYLIST IMPROVEMENT
		//TODO test this in alpha
		/*Sometimes, addChild methods r throwing an error when doing addChild in property declaration:
		 *private var myChild:DisplayObject = addChild(new DisplayObject());
		 */
		
		//TODO fix REMOVED event not fired when a child is addChilded directly in an other sprite
		//it means also to update the displaylist array...
		
		/*
		
		private var displaylist:Array = new Array();
			public function get children():Array
			{
				if (displaylist == null) { displaylist = new Array(); }
				return displaylist;
			}
		
		override public function addChild(child:DisplayObject):DisplayObject 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			displaylist.push(child);
			return super.addChild(child);
		}
		
		override public function addChildAt(child:DisplayObject, index:int):DisplayObject 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			displaylist.splice(1, 0, child);
			return super.addChildAt(child, index);
		}
		
		public function addChildren(children:Array):Array
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			for (var i:int = 0; i < children.length; i++) { addChild(children[i]); }
			return children;
		}
		
		public function addChildrenAt(children:Array, index:int):Array
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			for (var i:int = 0; i < children.length; i++) { addChildAt(children[i], index + i); }
			return children;
		}
		
		override public function contains(child:DisplayObject):Boolean 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			var i:int = numChildren;
			while (--i) { if (displaylist[i] == child) { return true; } }
			
			return false;
		}
		
		override public function getChildAt(index:int):DisplayObject
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			return displaylist[index];
		}
		
		override public function getChildIndex(child:DisplayObject):int 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			var i:int = numChildren;
			while (--i) { if (displaylist[i] == child) { break; } }
			
			return i;
		}
		
		override public function removeChild(child:DisplayObject):DisplayObject 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			var i:int = numChildren;
			while (--i) { if (displaylist[i] == child) { return removeChildAt(i); } }
			
			return null;
		}
		
		override public function removeChildAt(index:int):DisplayObject 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			displaylist.splice(index, 1);
			return super.removeChildAt(index);
		}
		
		public function removeChildren(children:Array = null):Array
		{
			if (displaylist == null) { displaylist = new Array(); }
			if (children == null) { children = displaylist.concat(); }
			
			for (var i:int = 0; i < children.length; i++) { removeChild(children[i]); }
			return children;
		}
		
		public function removeChildrenAt(num:int, index:int):Array
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			for (var i:int = 0; i < num; i++) { removeChildAt(index + i); }
			return children;
		}
		
		override public function setChildIndex(child:DisplayObject, index:int):void 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			addChildAt(removeChild(child), index);
		}
		
		override public function swapChildren(child1:DisplayObject, child2:DisplayObject):void 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			var i:int = numChildren, j:int = numChildren;
				while (--i) { if (displaylist[i] == child1) { break; } }
				while (--j) { if (displaylist[j] == child2) { break; } }
			
			return swapChildrenAt(i, j);
		}
		
		override public function swapChildrenAt(index1:int, index2:int):void 
		{
			if (displaylist == null) { displaylist = new Array(); }
			
			var tmp:* = displaylist[index1];
				displaylist[index1] = displaylist[index2];
				displaylist[index2] = tmp;
			
			return super.swapChildrenAt(index1, index2);
		}
		
		override public function get numChildren():int
		{
			if (displaylist == null) { displaylist = new Array(); }
				return displaylist.length;
		}

		*/

		
		
		
		
		
		//UTILS
		override public function toString():String 	{ return "[Smartie " + name + " (x=" + x + ", y=" + y + ", width=" + Maths.trim(width, 2) + ", height=" + Maths.trim(height, 2) + ", scaleX=" + Maths.trim(scaleX, 2) + ", scaleY=" + Maths.trim(scaleY, 2) + ") ]"; }
	}
}